package com.dj.ms.configure;

import cn.hutool.core.util.RandomUtil;
import com.dj.ms.enums.OsPlatformEnum;
import com.dj.ms.utils.OsUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * <br>
 * <p>
 * - 环境配置processor
 * </p>
 *
 * <br>
 *
 * @author ZJG
 * @version 1.0
 * @date 2021/11/9 下午3:29
 */
@Slf4j
public class MsEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static AtomicBoolean initFag = new AtomicBoolean(false);

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (initFag.get()) {
            return;
        }
        if (OsPlatformEnum.Windows.equals(OsUtils.getOSname())) {
            return;
        }
        Properties properties = new Properties();
        properties.put("management.server.port", getUsablePort());
        System.out.println("The microservice management server port is: " + properties.get("management.server.port"));
        PropertiesPropertySource source = new PropertiesPropertySource("source", properties);
        environment.getPropertySources().addLast(source);
        initFag.set(true);
    }

    /**
     * 获取可用端口
     * @return port
     */
    private int getUsablePort() {
        //系统动态端口一般为：49152到65535
        //linux可通过sysctl.conf配置
        int min = 49153, max = 65534;
        Random random = RandomUtil.getRandom(true);
        int port = random.nextInt(max) % (max - min + 1) + min;
        boolean using = portUsed(port);
        if (using) {
            return getUsablePort();
        } else {
            return port;
        }
    }

    private Boolean portUsed(Integer port) {
        String[] netStatStr;
        StringBuilder sb = null;
        OsPlatformEnum oSname = OsUtils.getOSname();

        if (OsPlatformEnum.Mac_OS_X.equals(oSname) || OsPlatformEnum.Mac_OS.equals(oSname)) {
            netStatStr = new String[]{"/bin/sh", "-c", "netstat -an | grep " + port};
        } else if (OsPlatformEnum.Windows.equals(oSname)) {
//            netStatStr = new String[]{"cmd", "-c", "netstat -ano | findstr " + port};
            return false;
        } else {
            netStatStr = new String[]{"/bin/sh", "-c", "netstat -ano | grep " + port};
        }

        try {
            // 利用Runtime类获得运行时环境去执行一个netstat的命令
            // 该exec()方法返回的Process类型数据有一个getInputStream()方法，获得其输入流拿到命令查询结果字符串
            InputStream inputstream = Runtime.getRuntime().exec(netStatStr).getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputstream));

            // sb就是最后拿到的查询结果，最后可以处理字符串拿到占用这个端口号的外部ip
            sb = new StringBuilder();
            String temp;
            while (null != (temp = bufferedReader.readLine())) {
                sb.append(temp + "\n");
            }
        } catch (IOException e) {
            System.out.println("MsEnvironmentPostProcessor.portUsed error");
            return false;
        }
        sb.trimToSize();
        return sb.length() > 2;
    }
}
